
#ifndef __SKINNING_H__
#define __SKINNING_H__

#include <vector>
#include <queue>
#include "mesh.h"

/// This class implements operations to bind the 
/// skeleton on the mesh model
class Skinning {
   
struct lflist_element {
   
   lflist_element() :
      vertex(0), e_init(0), e_term(0){}
   
   lflist_element(const int v, const int ei, const int et) :
      vertex(v), e_init(ei), e_term(et){}
      
   int        vertex;
   int        e_init;
   int        e_term;
};
   
struct stack_element {
   
   stack_element() :
      vertex(0), e_init(0), e_term(0), dist(0) {}
   
   stack_element(const int v, const int ei, const int et, const Number d) :
      vertex(v), e_init(ei), e_term(et), dist(d) {}
      
   int        vertex;
   int        e_init;
   int        e_term;
   Number      dist;  //< distance to connected component (key)
};

struct compare_stackelem {
   bool operator() (const stack_element &a, const stack_element &b) const {
      return a.dist > b.dist;
   }
};

void print(stack_element &s) {
   cout<<"    :: "<<s.vertex<<" : "<<s.e_init<<" : "<<s.e_term<<" : "<<s.dist<<endl;
}

void print(lflist_element &s) {
   cout<<"    :: "<<s.vertex<<" : "<<s.e_init<<" : "<<s.e_term<<endl;
}
   
public:
   
   Skinning();
   Skinning(Mesh *m, int nclusters);
   
   /// For each mesh vertex defines a set of skeleton joints and its associated weights
   /// Prerequisite: the riggingControl functions has been called before
   void execute(Mesh *m);
   void execute();
   
   void set_diffusion_amplitude(double d)     { diffusion_amplitude = d; }
   void set_number_iterations(unsigned int i) { num_iterations = i; }
   void set_number_expansions(unsigned int i) { num_expansions = i; }
   
private:
   void find_connected_component(int id, int current_component);
   void propagate_rigging_group(int bone, int current_group);
   
   void compute_normalized_gaussian_table();
   Number diffusion_function(int diffusion_option, Number dist);
   Number length(int id_he) const;

   //~ void correctClosestJoints(MeshWithSkeleton::Vertex_handle v);
   //~ int Initial_vertex_invisible(int v);
   //~ int Vertex_invisible(int v, int xtriang_vertex);
   //~ bool interTriSeg(const Vector3 &u, const Vector3 &v, const Vector3 &dir, const Vector3 &w0);
   
   ///
   void find_connected_component_lf(int v, int current_component);
   void weightsLaplacianDiffusion(int bone, int current_group);
   void computeLaplacianFilter(Vertex_handle v, int bone, int s);
   
   void init_vars();
   
private:
   typedef priority_queue<stack_element, vector<stack_element>, compare_stackelem>   stack;
   
   Mesh              *model;  /// the model represented by a mesh and a skeleton
   vector<stack>     stack_search;
   
   int                 limit_weights, list_size;  //< size of the list used to find a component by horizontal search
   int                 num_vertices, num_bones;   
   
   // new skinning variables
   typedef vector<lflist_element>     lflist; // Laplacian filter list type
   vector<lflist>                     lflist_bones;
   unsigned int                       num_iterations; // max number of Laplacian filter iterations
   unsigned int                       num_expansions;
   
   int                        standard_deviation;
   int                        diffusion_function_option;
   Number                     diffusion_amplitude;
   int                        diffusion_precision;
   vector<int>                normalized_gaussian_table;
   
   int                        nBones;
};

#endif
